//------------------------------------------------------------------------------
// Copyright (c) 2008 www.dnaide.com
// Licensed under the MIT (MIT-LICENSE.txt)
//------------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Web;
using System.Web.UI;
using System.Reflection;
using System.Text.RegularExpressions;
using DNAide.Core;
using DNAide.Configuration;
using DNAide.Web.UI.Scripts.Minifier;
using DNAide.Web.UI.Scripts.Packer;

namespace DNAide.Web.UI.Scripts
{

	/// <summary>
	/// Helper class for registering / obfuscating scripts
	/// </summary>
	public static class ScriptAide
	{

		#region Static

		private static String[] _resourcePaths;

		internal static String[] ResourcePaths
		{
			get { return _resourcePaths; }
		}

		static ScriptAide()
		{
			_resourcePaths = new String[] {
				"DNAide.Web.UI.Scripts.Resources.SwfObject.swfobject.js",
				"DNAide.Web.UI.Scripts.Resources.ImagePreloader.imagepreloader.js",
				"DNAide.Web.UI.Scripts.Resources.JQuery.jquery.js",
				"DNAide.Web.UI.Scripts.Resources.JQuery.DNAidePlugins.StringAide.stringaide.js",
				"DNAide.Web.UI.Scripts.Resources.JQuery.DNAidePlugins.ValidationAide.validationaide.js",
				"DNAide.Web.UI.Scripts.Resources.JQuery.DNAidePlugins.DefaultButton.defaultbutton.js",
				"DNAide.Web.UI.Scripts.Resources.JQuery.ThirdPartyPlugins.DatePicker.datepicker.js",
				"DNAide.Web.UI.Scripts.Resources.JQuery.ThirdPartyPlugins.Dimensions.dimensions.js",
				"DNAide.Web.UI.Scripts.Resources.XmlStringParser.xmlstringparser.js",
				"DNAide.Web.UI.Scripts.Resources.JQuery.DNAidePlugins.AjaxAide.ajaxaide.js",
				"DNAide.Web.UI.Scripts.Resources.Date.date.js",
				"DNAide.Web.UI.Scripts.Resources.JQuery.ThirdPartyPlugins.BgIFrame.bgiframe.js",
				"DNAide.Web.UI.Scripts.Resources.JQuery.jquery_1_1.js"
			};
		}

		private static ScriptAideContext CurrentContext
		{
			get
			{
				return (HttpContext.Current.Items["DNAideScriptAideContext"] ?? (
						HttpContext.Current.Items["DNAideScriptAideContext"] =
						new ScriptAideContext(HttpContext.Current.CurrentHandler as Page))) as ScriptAideContext;
			}
		}

		internal static string CacheUrl
		{
			get
			{
				return DNAide.Configuration.DNAideConfigurationSettings.Web.CacheUrl + "/Scripts";
			}
		}

		internal static string EnsureCachePhysicalLocationExists()
		{
			string retval = Path.Combine(DNAide.Configuration.DNAideConfigurationSettings.Web.CachePhysicalPath, "Scripts");
			if (!Directory.Exists(retval))
			{
				Directory.CreateDirectory(retval);
			}
			return retval;
		}


		/// <summary>
		/// Default register location type. By default in the &lt;head&gt; of the document
		/// </summary>
		public static ScriptRegisterLocationType DefaultRegisterLocationType
		{
			get { return ScriptAide.CurrentContext.DefaultRegisterLocationType; }
			set { ScriptAide.CurrentContext.DefaultRegisterLocationType = value; }
		}


		/// <summary>
		/// Default control if LocationType = Control
		/// </summary>
		public static Control DefaultControl
		{
			get { return ScriptAide.CurrentContext.DefaultControl; }
			set { ScriptAide.CurrentContext.DefaultControl = value; }
		}


		/// <summary>
		/// Default obfuscate type. Default = None
		/// </summary>
		public static ScriptObfuscateType DefaultObfuscateType
		{
			get { return ScriptAide.CurrentContext.DefaultObfuscateType; }
			set { ScriptAide.CurrentContext.DefaultObfuscateType = value; }
		}


		/// <summary>
		/// Whether to batch includes / DNAide resources into one file. Default = False
		/// </summary>
		public static Boolean BatchIncludes
		{
			get { return ScriptAide.CurrentContext.BatchIncludes; }
			set { ScriptAide.CurrentContext.BatchIncludes = value; }
		}


		/// <summary>
		/// Whether to return includes / DNAide resources as a gzip/deflate response
		/// </summary>
		public static Boolean DefaultCompressIncludes
		{
			get { return ScriptAide.CurrentContext.UseCompressionHandler; }
			set { ScriptAide.CurrentContext.UseCompressionHandler = value; }
		}


		/// <summary>
		/// Register script details
		/// </summary>
		/// <param name="details"></param>
		public static void Register(ScriptDetails details)
		{
			ScriptAide.CurrentContext.AddScriptToRegister(details);
		}


		/// <summary>
		/// Register embedded script using default values
		/// </summary>
		/// <param name="script"></param>
		public static void RegisterEmbed(String script)
		{
			RegisterEmbed(script, ScriptAide.DefaultRegisterLocationType, ScriptAide.DefaultControl);
		}


		/// <summary>
		/// Register embedded script in a specified control
		/// </summary>
		/// <param name="script"></param>
		/// <param name="control"></param>
		public static void RegisterEmbed(String script, Control control)
		{
			RegisterEmbed(script, ScriptRegisterLocationType.Control, control);
		}


		/// <summary>
		/// Register embedded script at a specified location
		/// </summary>
		/// <param name="script"></param>
		/// <param name="registerLocationType"></param>
		public static void RegisterEmbed(String script, ScriptRegisterLocationType registerLocationType)
		{
			RegisterEmbed(script, registerLocationType, ScriptAide.DefaultControl);
		}


		/// <summary>
		/// Register embedded script at a specified location
		/// </summary>
		/// <param name="script"></param>
		/// <param name="registerLocationType"></param>
		/// <param name="control"></param>
		public static void RegisterEmbed(String script, ScriptRegisterLocationType registerLocationType, Control control)
		{
			RegisterEmbed(script, registerLocationType, control, new Dictionary<Regex, string>());
		}


		/// <summary>
		/// Register embedded script at a specified location
		/// </summary>
		/// <param name="script"></param>
		/// <param name="registerLocationType"></param>
		/// <param name="control"></param>
		/// <param name="replacementRules"></param>
		public static void RegisterEmbed(String script, ScriptRegisterLocationType registerLocationType, Control control, Dictionary<Regex, String> replacementRules)
		{
			ScriptDetails details = new ScriptDetails();
			details.ScriptType = ScriptType.Embed;
			details.RegisterLocation = registerLocationType;
			details.Control = control;
			details.Key = StringAide.ToHash("DNAideScriptAideEmbed" + script, HashAlgorithmType.MD5);
			details.Value = script;
			details.ObfuscateType = ScriptAide.DefaultObfuscateType;
			details.ReplacementRules = replacementRules;
			ScriptAide.CurrentContext.AddScriptToRegister(details);
		}


		/// <summary>
		/// Register include scripts using default values.
		/// </summary>
		/// <param name="scriptUrl"></param>
		public static void RegisterInclude(String scriptUrl)
		{
			RegisterInclude(scriptUrl, ScriptAide.DefaultRegisterLocationType, ScriptAide.DefaultControl);
		}


		/// <summary>
		/// Register include script in a specified control
		/// </summary>
		/// <param name="scriptUrl"></param>
		/// <param name="control"></param>
		public static void RegisterInclude(String scriptUrl, Control control)
		{
			RegisterInclude(scriptUrl, ScriptRegisterLocationType.Control, control);
		}


		/// <summary>
		/// Register include scripts at a specified location
		/// </summary>
		/// <param name="scriptUrl"></param>
		/// <param name="registerLocationType"></param>
		public static void RegisterInclude(String scriptUrl, ScriptRegisterLocationType registerLocationType)
		{
			RegisterInclude(scriptUrl, registerLocationType, ScriptAide.DefaultControl);
		}


		/// <summary>
		/// Register include scripts at a specified location
		/// </summary>
		/// <param name="scriptUrl"></param>
		/// <param name="registerLocationType"></param>
		/// <param name="control"></param>
		public static void RegisterInclude(String scriptUrl, ScriptRegisterLocationType registerLocationType, Control control)
		{
			RegisterInclude(scriptUrl, registerLocationType, control, new Dictionary<Regex,string>());
		}


		/// <summary>
		/// Register include scripts at a specified location
		/// </summary>
		/// <param name="scriptUrl"></param>
		/// <param name="registerLocationType"></param>
		/// <param name="control"></param>
		/// <param name="replacementRules"></param>
		public static void RegisterInclude(String scriptUrl, ScriptRegisterLocationType registerLocationType, Control control, Dictionary<Regex, String> replacementRules)
		{
			ScriptDetails details = new ScriptDetails();
			details.ScriptType = ScriptType.Include;
			details.RegisterLocation = registerLocationType;
			details.Control = control;
			details.Key = StringAide.ToHash("DNAideScriptAideInclude" + scriptUrl, HashAlgorithmType.MD5); ;
			details.Value = scriptUrl;
			details.ObfuscateType = ScriptAide.DefaultObfuscateType;
			details.ReplacementRules = replacementRules;
			ScriptAide.CurrentContext.AddScriptToRegister(details);
		}


		/// <summary>
		/// Register include scripts using default values.
		/// Script will not be copied to the cache. No ObfuscateType will take place.
		/// </summary>
		/// <param name="scriptUrl"></param>
		public static void RegisterIncludeNoCache(String scriptUrl)
		{
			RegisterIncludeNoCache(scriptUrl, ScriptAide.DefaultRegisterLocationType, ScriptAide.DefaultControl);
		}


		/// <summary>
		/// Register include script in a specified control.
		/// Script will not be copied to the cache. No ObfuscateType will take place.
		/// </summary>
		/// <param name="scriptUrl"></param>
		/// <param name="control"></param>
		public static void RegisterIncludeNoCache(String scriptUrl, Control control)
		{
			RegisterIncludeNoCache(scriptUrl, ScriptRegisterLocationType.Control, control);
		}


		/// <summary>
		/// Register include scripts at a specified location.
		/// Script will not be copied to the cache. No ObfuscateType will take place.
		/// </summary>
		/// <param name="scriptUrl"></param>
		/// <param name="registerLocationType"></param>
		public static void RegisterIncludeNoCache(String scriptUrl, ScriptRegisterLocationType registerLocationType)
		{
			RegisterIncludeNoCache(scriptUrl, registerLocationType, ScriptAide.DefaultControl);
		}


		/// <summary>
		/// Register include scripts at a specified location.
		/// Script will not be copied to the cache. No ObfuscateType will take place.
		/// </summary>
		/// <param name="scriptUrl"></param>
		/// <param name="registerLocationType"></param>
		/// <param name="control"></param>
		public static void RegisterIncludeNoCache(String scriptUrl, ScriptRegisterLocationType registerLocationType, Control control)
		{
			RegisterIncludeNoCache(scriptUrl, registerLocationType, control, new Dictionary<Regex, string>());
		}


		/// <summary>
		/// Register include scripts at a specified location.
		/// Script will not be copied to the cache. No ObfuscateType will take place.
		/// </summary>
		/// <param name="scriptUrl"></param>
		/// <param name="registerLocationType"></param>
		/// <param name="control"></param>
		/// <param name="replacementRules"></param>
		public static void RegisterIncludeNoCache(String scriptUrl, ScriptRegisterLocationType registerLocationType, Control control, Dictionary<Regex, String> replacementRules)
		{
			ScriptDetails details = new ScriptDetails();
			details.ScriptType = ScriptType.IncludeNoCache;
			details.RegisterLocation = registerLocationType;
			details.Control = control;
			details.Key = StringAide.ToHash("DNAideScriptAideInclude" + scriptUrl, HashAlgorithmType.MD5); ;
			details.Value = scriptUrl;
			details.ObfuscateType = ScriptAide.DefaultObfuscateType;
			details.ReplacementRules = replacementRules;
			ScriptAide.CurrentContext.AddScriptToRegister(details);
		}

		

		/// <summary>
		/// Register DNAide resource includes using default values.
		/// </summary>
		/// <param name="dnaideScriptResource"></param>
		public static void RegisterDNAideResource(DNAideScriptResource dnaideScriptResource)
		{
			RegisterDNAideResource(dnaideScriptResource, ScriptAide.DefaultRegisterLocationType, ScriptAide.DefaultControl);
		}


		/// <summary>
		/// Register DNAide resource in a specified control
		/// </summary>
		/// <param name="dnaideScriptResource"></param>
		/// <param name="control"></param>
		public static void RegisterDNAideResource(DNAideScriptResource dnaideScriptResource, Control control)
		{
			RegisterDNAideResource(dnaideScriptResource, ScriptRegisterLocationType.Control, control);
		}


		/// <summary>
		/// Register DNAide resource at a specified location.
		/// </summary>
		/// <param name="dnaideScriptResource"></param>
		/// <param name="registerLocationType"></param>
		public static void RegisterDNAideResource(DNAideScriptResource dnaideScriptResource, ScriptRegisterLocationType registerLocationType)
		{
			RegisterDNAideResource(dnaideScriptResource, registerLocationType, ScriptAide.DefaultControl);
		}


		/// <summary>
		/// Register DNAide resource at a specified location.
		/// </summary>
		/// <param name="dnaideScriptResource"></param>
		/// <param name="registerLocationType"></param>
		/// <param name="control"></param>
		public static void RegisterDNAideResource(DNAideScriptResource dnaideScriptResource, ScriptRegisterLocationType registerLocationType, Control control)
		{
			RegisterDNAideResource(dnaideScriptResource, registerLocationType, control, new Dictionary<Regex, string>());
		}


		/// <summary>
		/// Register DNAide resource at a specified location.
		/// </summary>
		/// <param name="dnaideScriptResource"></param>
		/// <param name="registerLocationType"></param>
		/// <param name="control"></param>
		/// <param name="replacementRules"></param>
		public static void RegisterDNAideResource(DNAideScriptResource dnaideScriptResource, ScriptRegisterLocationType registerLocationType, Control control, Dictionary<Regex, String> replacementRules)
		{
			ScriptDetails details = new ScriptDetails();
			details.ScriptType = ScriptType.DNAideResource;
			details.RegisterLocation = registerLocationType;
			details.Control = control;
			details.Key = StringAide.ToHash("DNAideScriptAideResources" + dnaideScriptResource.ToString(), HashAlgorithmType.MD5); ;
			details.Value = dnaideScriptResource.ToString();
			details.ObfuscateType = ScriptAide.DefaultObfuscateType;
			details.ReplacementRules = replacementRules;
			ScriptAide.CurrentContext.AddScriptToRegister(details);
		}


		internal static String Minify(string script)
		{
			ScriptMinifier minifier = new ScriptMinifier();
			return minifier.Minify(script);
		}

		internal static String Pack(string script)
		{
			ECMAScriptPacker packer = new ECMAScriptPacker(ECMAScriptPacker.PackerEncoding.Numeric, true, false);
			return packer.Pack(script);
		}


		/// <summary>
		/// Obfuscate a script
		/// </summary>
		/// <param name="script"></param>
		/// <param name="obfuscateType"></param>
		/// <returns></returns>
		public static String Obfuscate(string script, ScriptObfuscateType obfuscateType)
		{
			script = CastAide.AsString(script);
			switch (obfuscateType)
			{
				case ScriptObfuscateType.Minify:
					script = ScriptAide.Minify(script);
					break;
				case ScriptObfuscateType.Pack:
					script = ScriptAide.Pack(script);
					break;
			}
			return script;
		}

		#endregion

	}


	internal class ScriptAideContext
	{
		private object _lock = new object();
		private Page _page;
		private ScriptObfuscateType _defaultObfuscateType;
		private ScriptRegisterLocationType _defaultRegisterLocationType;
		private Control _defaultControl;
		private bool _batchIncludes;
		private bool _useCompressionHandler;
		private List<string> _uniqueKeys;
		private Dictionary<Control, Dictionary<string, ScriptDetails>> _scriptsToRegister;

		internal ScriptObfuscateType DefaultObfuscateType
		{
			get { return this._defaultObfuscateType; }
			set { this._defaultObfuscateType = value; }
		}

		internal ScriptRegisterLocationType DefaultRegisterLocationType
		{
			get { return this._defaultRegisterLocationType; }
			set { this._defaultRegisterLocationType = value; }
		}

		internal Control DefaultControl
		{
			get { return this._defaultControl; }
			set { this._defaultControl = value; }
		}

		internal bool BatchIncludes
		{
			get { return this._batchIncludes; }
			set { this._batchIncludes = value; }
		}

		internal bool UseCompressionHandler
		{
			get { return this._useCompressionHandler; }
			set { this._useCompressionHandler = value; }
		}

		internal ScriptAideContext(Page page)
		{
			this._page = page;
			this._page.PreRenderComplete += new EventHandler(_page_PreRenderComplete);

			this._defaultObfuscateType = DNAideConfigurationSettings.Web.ScriptAide.DefaultObfuscateType;
			this._defaultRegisterLocationType = DNAideConfigurationSettings.Web.ScriptAide.DefaultRegisterLocationType;

			if (!string.IsNullOrEmpty(DNAideConfigurationSettings.Web.ScriptAide.DefaultControl))
			{
				this._defaultControl = PageAide.FindControl<Control>(this._page, DNAideConfigurationSettings.Web.ScriptAide.DefaultControl);
			}

			this._batchIncludes = DNAideConfigurationSettings.Web.ScriptAide.BatchIncludes;
			this._useCompressionHandler = DNAideConfigurationSettings.Web.ScriptAide.UseCompressionHandler;
			this._scriptsToRegister = new Dictionary<Control, Dictionary<string, ScriptDetails>>();
			this._uniqueKeys = new List<string>();
		}

		void _page_PreRenderComplete(object sender, EventArgs e)
		{
			foreach (Control registerControl in this._scriptsToRegister.Keys)
			{
				StringBuilder sb = new StringBuilder();
				StringBuilder embedSb = new StringBuilder();
				List<string> filesToBatch = new List<string>();

				StringBuilder batchFileName = new StringBuilder();

				foreach (KeyValuePair<string, ScriptDetails> kvp in this._scriptsToRegister[registerControl])
				{
					string cacheUrlFileName, cachePhysicalFileName, scriptText;

					switch (kvp.Value.ScriptType)
					{
						case ScriptType.DNAideResource:
							cacheUrlFileName = string.Format("{0}/{1}.{2}.js", ScriptAide.CacheUrl, kvp.Value.Value, StringAide.ToHash(kvp.Value.Value + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(), HashAlgorithmType.MD5));
							cachePhysicalFileName = HttpContext.Current.Server.MapPath(cacheUrlFileName);

							DNAideScriptResource resourceEnum = (DNAideScriptResource)Enum.Parse(typeof(DNAideScriptResource), kvp.Value.Value, true);

							// Create the physical file in the cache
							if (!File.Exists(cachePhysicalFileName))
							{
								lock (this._lock)
								{
									if (!File.Exists(cachePhysicalFileName))
									{
										ScriptAide.EnsureCachePhysicalLocationExists();
										scriptText = DNAide.Core.ResourcesAide.GetEmbeddedDNAideTextFileResource(ScriptAide.ResourcePaths[(int)resourceEnum]);
										scriptText = ApplyGlobalReplacementRules(scriptText);
										scriptText = ApplyReplacementRules(kvp.Value.ReplacementRules, scriptText);
										File.WriteAllText(cachePhysicalFileName, ScriptAide.Obfuscate(scriptText, kvp.Value.ObfuscateType));
									}
								}
							}

							if (this.BatchIncludes)
							{
								filesToBatch.Add(cachePhysicalFileName);
								batchFileName.Append(cacheUrlFileName);
								batchFileName.Append(File.GetLastWriteTime(cachePhysicalFileName).Ticks);
							}
							else
							{
								sb.AppendFormat(Resources.ScriptAide.IncludeMarkup, this._page.ResolveUrl(cacheUrlFileName));
							}

							break;

						case ScriptType.Include:
							// Register include
							// Take a copy and move it into the cache if applicable
							string originalPhysicalFileName = HttpContext.Current.Server.MapPath(kvp.Value.Value);
							if (!File.Exists(originalPhysicalFileName))
							{
								throw new Exception(string.Format(Resources.ScriptAide.UnableToLocateInclude, originalPhysicalFileName));
							}
							cacheUrlFileName = string.Format("{0}/{1}.{2}.js", ScriptAide.CacheUrl, Path.GetFileName(originalPhysicalFileName), StringAide.ToHash(kvp.Value.Value + File.GetLastWriteTime(originalPhysicalFileName).Ticks.ToString(), HashAlgorithmType.MD5));
							cachePhysicalFileName = HttpContext.Current.Server.MapPath(cacheUrlFileName);

							// Create the physical file in the cache
							if (!File.Exists(cachePhysicalFileName))
							{
								lock (this._lock)
								{
									if (!File.Exists(cachePhysicalFileName))
									{
										ScriptAide.EnsureCachePhysicalLocationExists();
										scriptText = CastAide.AsString(File.ReadAllText(originalPhysicalFileName));
										scriptText = ApplyGlobalReplacementRules(scriptText);
										scriptText = ApplyReplacementRules(kvp.Value.ReplacementRules, scriptText);
										File.WriteAllText(cachePhysicalFileName, ScriptAide.Obfuscate(scriptText, kvp.Value.ObfuscateType));
									}
								}
							}

							if (this.BatchIncludes)
							{
								filesToBatch.Add(cachePhysicalFileName);
								batchFileName.Append(cacheUrlFileName);
								batchFileName.Append(File.GetLastWriteTime(cachePhysicalFileName).Ticks);
							}
							else
							{
								sb.AppendFormat(Resources.ScriptAide.IncludeMarkup, this._page.ResolveUrl(cacheUrlFileName));
							}
							break;


						case ScriptType.IncludeNoCache:
							// Register include but do not copy to the cache nor obfuscate nor batch
							// Take a copy and move it into the cache if applicable

							sb.AppendFormat(Resources.ScriptAide.IncludeMarkup, this._page.ResolveUrl(kvp.Value.Value));
							break;

						case ScriptType.Embed:
							// Register embedded script
							// If batching includes, ensure all includes are registered before any embedded code
							scriptText = ApplyGlobalReplacementRules(kvp.Value.Value);
							scriptText = ApplyReplacementRules(kvp.Value.ReplacementRules, kvp.Value.Value);
							if (this.BatchIncludes)
							{
								embedSb.AppendFormat(Resources.ScriptAide.InlineMarkup, ScriptAide.Obfuscate(scriptText, kvp.Value.ObfuscateType));
							}
							else
							{
								sb.AppendFormat(Resources.ScriptAide.InlineMarkup, ScriptAide.Obfuscate(scriptText, kvp.Value.ObfuscateType));
							}
							break;

					}
				}

				if (batchFileName.Length > 0)
				{
					string hashedBatchFileName = StringAide.ToHash(batchFileName.ToString(), HashAlgorithmType.MD5) + ".batched.js";

					string hashedBatchFileNameUrlPath = string.Format("{0}/{1}", ScriptAide.CacheUrl, hashedBatchFileName);
					string hashedBatchFileNamePhysicalPath = HttpContext.Current.Server.MapPath(hashedBatchFileNameUrlPath);
					if (!File.Exists(hashedBatchFileNamePhysicalPath))
					{
						lock (this._lock)
						{
							if (!File.Exists(hashedBatchFileNamePhysicalPath))
							{
								ScriptAide.EnsureCachePhysicalLocationExists();
								StringBuilder batchedFileContent = new StringBuilder();
								foreach (string fileToBatch in filesToBatch)
								{
									File.AppendAllText(hashedBatchFileNamePhysicalPath, File.ReadAllText(fileToBatch));
								}
							}
						}
					}

					if (this.UseCompressionHandler)
					{
						sb.AppendFormat(Resources.ScriptAide.IncludeMarkup, this._page.ResolveUrl("~/DNAide/ScriptAide.axd?action=compress&file=" + HttpContext.Current.Server.UrlEncode(hashedBatchFileName)));
					}
					else
					{
						sb.AppendFormat(Resources.ScriptAide.IncludeMarkup, this._page.ResolveUrl(hashedBatchFileNameUrlPath));
					}
				}

				LiteralControl lit = new LiteralControl(sb.ToString() + embedSb.ToString());
				registerControl.Controls.Add(lit);
			}
		}


		internal static string ApplyReplacementRules(Dictionary<Regex, string> replacementRules, string input)
		{
			string retval = input;
			foreach (KeyValuePair<Regex, string> replacementKvp in replacementRules)
			{
				retval = replacementKvp.Key.Replace(retval, replacementKvp.Value.Replace("~/", UrlAide.GetApplicationPathForwardSlashAppended()));
			}
			return retval;
		}


		internal static string ApplyGlobalReplacementRules(string input)
		{
			string retval = input;
			foreach (ScriptAideReplacementRuleConfig config in DNAideConfigurationSettings.Web.ScriptAide.GlobalReplacementRules)
			{
				retval = config.PatternMatchExpressionRegex.Replace(retval, config.ReplaceExpression.Replace("~/", UrlAide.GetApplicationPathForwardSlashAppended()));
			}
			return retval;
		}
	


		internal void AddScriptToRegister(ScriptDetails details)
		{
			if (details == null)
			{
				throw new ArgumentException("details");
			}

			if (this._uniqueKeys.Contains(details.Key))
			{
				return;
			}

			Control registerControl;
			switch (details.RegisterLocation)
			{
				case ScriptRegisterLocationType.Form:
					registerControl = this._page.Form;
					break;
				case ScriptRegisterLocationType.Head:
					registerControl = this._page.Header;
					break;
				default:
					registerControl = details.Control;
					break;
			}

			if (registerControl == null)
			{
				throw new ArgumentException(Resources.ScriptAide.CannotFindControl);
			}

			if (string.IsNullOrEmpty(details.Key))
			{
				throw new ArgumentException(Resources.ScriptAide.KeyRequired);
			}

			if (!this._scriptsToRegister.ContainsKey(registerControl))
			{
				this._scriptsToRegister.Add(registerControl, new Dictionary<string, ScriptDetails>());
			}
			if (!this._scriptsToRegister[registerControl].ContainsKey(details.Key))
			{
				this._scriptsToRegister[registerControl].Add(details.Key, details);
			}

			this._uniqueKeys.Add(details.Key);
		}

	}


	/// <summary>
	/// Class to hold script detail information for register purposes
	/// </summary>
	public class ScriptDetails
	{

		private ScriptType _scriptType;
		private ScriptRegisterLocationType _registerLocation;
		private Control _control;
		private string _key;
		private string _value;
		private ScriptObfuscateType _obfuscateType;
		private Dictionary<Regex, string> _replacementRules;

		/// <summary>
		/// ScriptType
		/// </summary>
		public ScriptType ScriptType
		{
			get { return this._scriptType; }
			set { this._scriptType = value; }
		}

		/// <summary>
		/// Where in the page the script is to be registered
		/// </summary>
		public ScriptRegisterLocationType RegisterLocation
		{
			get { return this._registerLocation; }
			set { this._registerLocation = value; }
		}

		/// <summary>
		/// If the script is to be regsitered in a control
		/// </summary>
		public Control Control
		{
			get { return this._control; }
			set { this._control = value; }
		}

		/// <summary>
		/// Key identifier
		/// </summary>
		public String Key
		{
			get { return this._key; }
			set { this._key = value; }
		}

		/// <summary>
		/// Value, either the script, url of DNAide resource type
		/// </summary>
		public String Value
		{
			get { return this._value; }
			set { this._value = value; }
		}

		/// <summary>
		/// The obfuscate method to use
		/// </summary>
		public ScriptObfuscateType ObfuscateType
		{
			get { return this._obfuscateType; }
			set { this._obfuscateType = value; }
		}

		public Dictionary<Regex, string> ReplacementRules
		{
			get
			{
				if (this._replacementRules == null)
				{
					this._replacementRules = new Dictionary<Regex, string>();
				}
				return this._replacementRules;
			}
			set
			{
				this._replacementRules = value;
			}
		}

		/// <summary></summary>
		public ScriptDetails()
		{
		}

	}

}
